package com.example.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import com.example.demo.config.enter.CustomSecuityError;
import com.example.demo.config.jwt.JwtAuthenticationFilter;
import com.example.demo.config.jwt.JwtLoginFilter;
import com.example.demo.config.logout.CustomLogout;
import com.example.demo.service.UserServices;

/**
 * 安全策略，jwt用不到自定义的那写验证，全删了，留一些有用的
 */
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

	@Bean
	UserDetailsService userService() {
		return new UserServices();
	}

	@Bean
	DaoAuthenticationProvider authenticationProvider() {
		DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
		// 设置是否隐藏用户为空时的异常
		provider.setHideUserNotFoundExceptions(false);
		// user服务类
		provider.setUserDetailsService(userService());
		// 设置密码类型
		provider.setPasswordEncoder(passwordEncoder());
		return provider;
	}

	/**
	 * authorizeRequests是指定制请求的一些东西
	 * 
	 * @param http
	 * @throws Exception
	 */
	@Override
	protected void configure(HttpSecurity http) throws Exception {

		/**
		 * jwt 的hander是不允许所有 url访问，全部都携带 token
		 */
		http.authorizeRequests().anyRequest().authenticated();

		// 跨站
		http.csrf().disable();

		// security的异常处理
		http.exceptionHandling().authenticationEntryPoint(new CustomSecuityError());

		// 添加自定义登陆拦截器
		http.addFilterAt(jwt(), UsernamePasswordAuthenticationFilter.class);

		// 新定义的注销处理
		http.logout().addLogoutHandler(new CustomLogout()).logoutSuccessHandler(new CustomLogout());

		/**
		 * jwt拦截器，这里需要加入，authenticationManager，让spring security本身的身份认证管理器来工作
		 */
		http.addFilter(new JwtAuthenticationFilter(authenticationManager()));
	}

	// jwt，只考虑拦截login请求
	@Bean
	JwtLoginFilter jwt() throws Exception {
		JwtLoginFilter filter = new JwtLoginFilter(authenticationManager());
		// 拦截login的请求，和请求类型
		filter.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/login", "POST"));

		// 这句很关键，重用WebSecurityConfigurerAdapter配置的AuthenticationManager，不然要自己组装AuthenticationManager
		filter.setAuthenticationManager(authenticationManagerBean());
		return filter;
	}

	/**
	 * 题外话，如果搭自己的oauth2的server，需要让spring security
	 * oauth2共享同一个AuthenticationManager（源码的解释是这样写可以暴露出这个AuthenticationManager，也就是注册到spring
	 * ioc）
	 */
	@Override
	@Bean // share AuthenticationManager for web and oauth
	public AuthenticationManager authenticationManagerBean() throws Exception {
		return super.authenticationManagerBean();
	}

	// 关闭加密，开发中不推荐
	@Bean
	public static NoOpPasswordEncoder passwordEncoder() {
		return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
	}
}
